package com.example.threadcode.higherLeverTwo.SemaphoreDemo;

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;

/**
 * 默认主方法创建一个资源池,一个runnable用来重复获取和归还资源,还有一个线程执行(executor)数组,每个执行都去执行runnable
 * 当acquire()被调用的时候 没有加同步锁.因为加锁会阻止返回的item返回到资源池中.然而String getNextAvailableItem()和
 * boolean markAsused(String item)都是加同步锁去维持资源池的连续性.
 */
public class SemaphoreDemo
{
   public static void main(String[] args)
   {
      final Pool pool = new Pool();
      Runnable r = new Runnable()
                   {
                      @Override
                      public void run()
                      {
                         String name = Thread.currentThread().getName();
                         try
                         {
                            while (true)
                            {
                               String item;
                               System.out.println(name + " acquiring " + 
                                                  (item = pool.getItem()));
                               Thread.sleep(200 + 
                                            (int) (Math.random() * 100));
                               System.out.println(name + " putting back " + 
                                                  item);
                               pool.putItem(item);
                            }
                         }
                         catch (InterruptedException ie)
                         {
                            System.out.println(name + "interrupted");
                         }
                      }
                   };
      ExecutorService[] executors = 
         new ExecutorService[Pool.MAX_AVAILABLE + 1];
      for (int i = 0; i < executors.length; i++)
      {
//         创建了11个线程,资源许可只有10,演示一个被阻塞
         executors[i] = Executors.newSingleThreadExecutor();
         executors[i].execute(r);
      }
   }
}

/**
 *   Pool类的getItem()和putItem()获得并返回基于字符串的资源.
 *   在getItem方法获得一个item之前,调用的线程必须获得一个来自semaphore的许可,这确保了获得的item是可用的
 *   然后线程调用putItem(String)方法,这把item返回到资源池中,然后释放许可证到semaphore中,这使得另一个线程可以获得这个许可.
 *
 *   */
final class Pool 
{
   public static final int MAX_AVAILABLE = 10;

   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

   private final String[] items;

   private final boolean[] used = new boolean[MAX_AVAILABLE];

   Pool()
   {
      items = new String[MAX_AVAILABLE];
      for (int i = 0; i < items.length; i++)
         items[i] = "I" + i;
   }

   String getItem() throws InterruptedException 
   {
      available.acquire();
      return getNextAvailableItem();
   }

   void putItem(String item) 
   {
      if (markAsUnused(item))
         available.release();
   }

   private synchronized String getNextAvailableItem() 
   {
      for (int i = 0; i < MAX_AVAILABLE; i++)
      {
         if (!used[i]) 
         {
            used[i] = true;
            return items[i];
         }
      }
      return null; // not reached
   }

   private synchronized boolean markAsUnused(String item) 
   {
      for (int i = 0; i < MAX_AVAILABLE; i++)
      {
         if (item == items[i]) 
         {
            if (used[i]) 
            {
               used[i] = false;
               return true;
            } 
            else
               return false;
         }
      }
      return false;
   }
}